package com.kun.framework.core.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import com.kun.framework.core.controller.AbstractController;
import com.kun.framework.core.exception.Assert;
import com.kun.framework.core.exception.BizException;
import com.kun.framework.core.util.ComUtil;
import com.kun.framework.core.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 核心类
 * bean对象上下文容器
 *
 * @author gzc
 * @since 2024/8/21
 */
public abstract class BeanContext {
    private static final Logger log = LoggerFactory.getLogger(BeanContext.class);
    /**
     * 扫描的基础包路径
     */
    private static final String BASE_PACKAGE = "com.kun";
    /**
     * 懒加载的类集合
     */
    private static final Set<Class<?>> LAZY_LOADING_SET = new HashSet<>(16);
    /**
     * 禁止加载的类集合
     */
    private static final Set<Class<?>> DISABLE_LOADING_SET = new HashSet<>(16);
    /**
     * 加载信息集合
     */
    private static final Map<LoadInfoKeyEnum, Set<Class<?>>> LOAD_INFO_MAP = new HashMap<>(16);
    /**
     * 存放{@link BeanInstance} 所有实现类的集合
     */
    private static final Map<Class<?>, BeanInstance> CONTEXT_MAP = new ConcurrentHashMap<>(16);

    /**
     * 加载{@link Initialize} 所有实现类，放入{@link #CONTEXT_MAP} 集合中，
     * 调用所有实现类的{@link Initialize#init()}方法
     */
    public static void init() {
        try {
            // 获取包名下所有类
            Set<Class<?>> clsSet = ClassUtil.scanPackageBySuper(BASE_PACKAGE, BeanInstance.class);
            for (Class<?> cls : clsSet) {
                // 抽象类和接口则跳过
                if (ClassUtil.isAbstractOrInterface(cls)) {
                    continue;
                }
                // 创建实例
                BeanInstance instance = newInstance(cls);
                if (instance == null) {
                    continue;
                }
                CONTEXT_MAP.put(cls, instance);
            }
        } catch (Throwable te) {
            if (te instanceof BizException be) {
                throw be;
            } else if (te instanceof Exception e) {
                throw new BizException("加载" + BASE_PACKAGE + "包下的所有" + Initialize.class.getSimpleName() + "子类发生异常", e);
            }
        }

        // 打印加载信息
        if (CollUtil.isNotEmpty(LOAD_INFO_MAP)) {
            LOAD_INFO_MAP.forEach((loadInfoKeyEnum, clsSet) -> log.info("{} -> {}", loadInfoKeyEnum.getPrintInfo(), JacksonUtil.toJsonStr(clsSet)));
        }

        // 调用init方法
        if (CollUtil.isNotEmpty(CONTEXT_MAP)) {
            for (BeanInstance instance : CONTEXT_MAP.values()) {
                Initialize initialize = convertInitialize(instance);
                if (initialize != null) {
                    try {
                        initialize.init();
                    } catch (Exception e) {
                        Assert.throwBizException(e, "初始化类->{}，失败", initialize);
                    }
                }
            }
        }
    }

    /**
     * 程序关闭前调用
     */
    public static void destroy() {
        if (CollUtil.isNotEmpty(CONTEXT_MAP)) {
            List<BeanInstance> controllerList = CONTEXT_MAP.values().stream().filter(beanInstance -> beanInstance instanceof AbstractController).toList();
            List<BeanInstance> otherList = CollUtil.subtractToList(CONTEXT_MAP.values(), controllerList);
            // 调用init方法
            if (CollUtil.isNotEmpty(controllerList)) {
                for (BeanInstance beanInstance : controllerList) {
                    if (beanInstance instanceof AbstractController abstractController) {
                        abstractController.stopCallback();
                    }
                    Destruction destruction = convertDestruction(beanInstance);
                    if (destruction != null) {
                        destruction.destroy();
                    }
                }
            }
            if (CollUtil.isNotEmpty(otherList)) {
                for (BeanInstance beanInstance : otherList) {
                    Destruction destruction = convertDestruction(beanInstance);
                    if (destruction != null) {
                        destruction.destroy();
                    }
                }
            }
        }
    }

    /**
     * 程序启动后调用
     */
    public static void startupBefore() {
        if (CollUtil.isNotEmpty(CONTEXT_MAP)) {
            for (BeanInstance instance : CONTEXT_MAP.values()) {
                Initialize initialize = convertInitialize(instance);
                if (initialize != null) {
                    try {
                        ComUtil.jfxThreadRun(initialize::startupBefore);
                    } catch (Exception e) {
                        Assert.throwBizException(e, "回调类->{}，失败", initialize);
                    }
                }
            }
        }
    }

    /**
     * 创建对象实例
     *
     * @param cls 类对象
     * @return 实例
     * @throws Exception 实例化异常
     */
    private static BeanInstance newInstance(Class<?> cls) throws Exception {
        // 创建对象实例
        BeanInstance instance = doNewInstance(cls);
        Assert.notNull(instance, "{}创建实例失败", cls);
        Initialize initialize = convertInitialize(instance);
        if (initialize != null) {
            boolean isLoading = true;
            if (initialize.disableLoading()) {
                isLoading = false;
                DISABLE_LOADING_SET.add(cls);
                putLoadInfo(LoadInfoKeyEnum.DISABLE, cls);
            }
            if (initialize.lazyLoading()) {
                isLoading = false;
                LAZY_LOADING_SET.add(cls);
                putLoadInfo(LoadInfoKeyEnum.LAZY, cls);
            }
            // 不加载则直接返回null
            if (!isLoading) {
                WeakReference<BeanInstance> weakReference = new WeakReference<>(instance);
                weakReference.clear();
                return null;
            }
        }

        // 加载成功
        putLoadInfo(LoadInfoKeyEnum.SUCCESS, cls);
        return instance;
    }

    /**
     * 反射调用无参构造函数创建实例
     *
     * @param cls 类对象
     * @return 实例
     * @throws Exception 反射异常
     */
    private static BeanInstance doNewInstance(Class<?> cls) throws Exception {
        // 反射调用无参构造函数创建实例
        return (BeanInstance) cls.getDeclaredConstructor().newInstance();
    }

    /**
     * 放入加载信息
     *
     * @param loadInfoKeyEnum 加载信息主键枚举
     * @param cls             类对象
     */
    private synchronized static void putLoadInfo(LoadInfoKeyEnum loadInfoKeyEnum, Class<?> cls) {
        Assert.notNull(loadInfoKeyEnum, "加载信息主键枚举为空");
        Assert.notNull(cls, "类对象为空");
        Set<Class<?>> clsSet = LOAD_INFO_MAP.get(loadInfoKeyEnum);
        if (CollUtil.isEmpty(clsSet)) {
            clsSet = new HashSet<>(16);
        }
        clsSet.add(cls);
        LOAD_INFO_MAP.put(loadInfoKeyEnum, clsSet);
    }

    /**
     * 加载信息主键枚举
     */
    private enum LoadInfoKeyEnum {
        /**
         * 加载成功
         */
        SUCCESS("加载成功"),
        /**
         * 加载失败
         */
        FAIL("加载失败"),
        /**
         * 懒加载
         */
        LAZY("懒加载"),
        /**
         * 禁止加载
         */
        DISABLE("禁止加载"),
        ;

        /**
         * 打印信息
         */
        private final String printInfo;

        LoadInfoKeyEnum(String printInfo) {
            this.printInfo = printInfo;
        }

        public String getPrintInfo() {
            return printInfo;
        }

        @Override
        public String toString() {
            return this.name();
        }
    }

    /**
     * 获取实例
     *
     * @param cls 类对象
     * @return 实例
     */
    @SuppressWarnings("ALL")
    public static <T extends BeanInstance> T get(Class<T> cls) {
        try {
            if (cls != null) {
                BeanInstance instance = CONTEXT_MAP.get(cls);
                // 不禁止且为懒加载，创建实例
                if (instance == null) {
                    if (!DISABLE_LOADING_SET.contains(cls) && LAZY_LOADING_SET.contains(cls)) {
                        // 双重校验锁
                        synchronized (BeanContext.class) {
                            instance = CONTEXT_MAP.get(cls);
                            if (instance == null) {
                                instance = doNewInstance(cls);
                                Initialize initialize = convertInitialize(instance);
                                initialize.init();
                                initialize.startupBefore();
                                CONTEXT_MAP.put(cls, instance);
                            }
                        }
                    }
                }
                return (T) instance;
            }
        } catch (Exception e) {
            Assert.throwBizException(e, "获取Bean对象【{}】失败", cls);
        }
        return null;
    }

    public static <T> Set<T> getAllImpl(Class<T> superCls) {
        Set<T> set = new HashSet<>(16);
        CONTEXT_MAP.forEach((cls, instance) -> {
            // 判断是否是子类
            if (superCls.isAssignableFrom(cls)) {
                set.add((T) instance);
            }
        });
        return set;
    }

    private static Initialize convertInitialize(BeanInstance beanInstance) {
        if (beanInstance instanceof Initialize initialize) {
            return initialize;
        }
        return null;
    }

    private static Destruction convertDestruction(BeanInstance beanInstance) {
        if (beanInstance instanceof Destruction destruction) {
            return destruction;
        }
        return null;
    }

}
